package cc.magicjson.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PortUtils {
    private static final Logger LOGGER = Logger.getLogger(PortUtils.class.getName());

    public static boolean isPortInUse(int port) {
        try (ServerSocket ignored = new ServerSocket(port)) {
            return false;
        } catch (IOException e) {
            return true;
        }
    }

    public static String[] updateArgsWithPort(String[] args, int port) {
        String[] newArgs = new String[args.length + 1];
        System.arraycopy(args, 0, newArgs, 0, args.length);
        newArgs[newArgs.length - 1] = "--server.port=" + port;
        return newArgs;
    }

    public static void terminateProcessOnPort(int port) {
        String os = System.getProperty("os.name").toLowerCase();
        try {
            if (os.contains("win")) {
                terminateProcessOnPortWindows(port);
            } else {
                terminateProcessOnPortUnix(port);
            }
        } catch (IOException | InterruptedException e) {
            LOGGER.log(Level.SEVERE, "Failed to terminate process on port " + port, e);
        }
    }

    private static void terminateProcessOnPortWindows(int port) throws IOException, InterruptedException {
        String findCommand = String.format("netstat -ano | findstr :%d", port);
        Process findProcess = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", findCommand});
        BufferedReader reader = new BufferedReader(new InputStreamReader(findProcess.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            if (line.contains("LISTENING")) {
                String[] parts = line.trim().split("\\s+");
                String pid = parts[parts.length - 1];
                String killCommand = String.format("taskkill /PID %s /F", pid);
                Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", killCommand}).waitFor();
            }
        }
        reader.close();
    }

    private static void terminateProcessOnPortUnix(int port) throws IOException, InterruptedException {
        String command = String.format("lsof -i :%d | grep LISTEN | awk '{print $2}' | xargs kill -9", port);
        Runtime.getRuntime().exec(new String[]{"sh", "-c", command}).waitFor();
    }

    public static void waitForPortToBeAvailable(int port) {
        while (isPortInUse(port)) {
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Thread interrupted while waiting for port to be available", e);
            }
        }
    }
}
